import { useMemo, useState } from 'react';
import { isArray, isFunction, isNumber, isString } from '../../utils/checkType';
export type Item = string | number | Record<string, any>;
export type SelectStatus = 'all' | 'none' | 'partially';
export type SelectType = 'checkbox' | 'radio';
export interface Options<T extends Item> {
  rowKey?: string;
  defaultSelected?: T[];
  selectType?: SelectType;
  isCanRepeatSelection?: boolean;
}
export interface Actions<T extends Item> {
  selectStatus: SelectStatus;
  getIsSelected: (item: T, selected?: T[]) => boolean;
  toggle: (item: T) => T[];
  toggleAll: () => T[];
  select: (item: T) => T[];
  unSelect: (item: T) => T[];
  selectAll: () => T[];
  unSelectAll: () => T[];
}

export const ALL_SELECTED = 'all';
export const NONE_SELECTED = 'none';
export const PARTIALLY_SELECTED = 'partially';

function useSelections<T extends Item>(
  selections: T[] | (() => T[]),
  options: Options<T> = {},
): [T[], Actions<T>] {
  // @ts-ignore
  const items = isFunction(selections) ? selections() : selections;
  const {
    defaultSelected = [],
    rowKey = 'key',
    selectType = 'checkbox',
  } = options;
  const [selected, setSelected] = useState<T[]>(defaultSelected);

  const find = (item: T, selected: T[]) => {
    if (isString(item) || isNumber(item)) {
      return selected.indexOf(item);
    } else {
      for (let i = 0; i < selected.length; i++) {
        const data = selected[i] as Record<string, any>;
        if (data[rowKey] === item[rowKey]) return i;
      }
      return -1;
    }
  };
  const selectStatus = useMemo(() => {
    const indexs = [];
    for (let i = 0; i < selected.length; i++) {
      const index = find(selected[i], items);
      if (index > -1) indexs.push(index);
    }
    if (!indexs.length) {
      return NONE_SELECTED;
    }

    if (indexs.length === items.length) {
      return ALL_SELECTED;
    }
    return PARTIALLY_SELECTED;
  }, [selected, items]);

  const getIsSelected = (item: T, selectedItems: T[] = selected) => {
    return find(item, selectedItems) > -1;
  };
  const add = (item: T | T[], selected: T[]) => {
    const items = isArray(item)
      ? item.filter((data) => !getIsSelected(data))
      : [item];
    const selectedData = selected.concat(items);
    setSelected(selectedData);
    return selectedData;
  };
  const remove = (item: T | T[], selected: T[]) => {
    const items = isArray(item) ? item : [item];
    const restSelected: T[] = [];
    for (let item of selected) {
      const i = find(item, items);
      if (i < 0) restSelected.push(item);
    }
    setSelected(restSelected);
    return restSelected;
  };
  const select = (item: T) => {
    return add(item, selectType === 'checkbox' ? selected : []);
  };
  const unSelect = (item: T) => {
    return remove(item, selected);
  };
  const selectAll = () => {
    return add(items, selected);
  };
  const unSelectAll = () => {
    return remove(items, selected);
  };
  const toggle = (item: T) => {
    if (selectType === 'checkbox') {
      const index = find(item, selected);
      return index > -1 ? remove(item, selected) : add(item, selected);
    } else {
      return add(item, []);
    }
  };
  const toggleAll = () => {
    if (selectStatus === ALL_SELECTED) {
      return remove(items, selected);
    } else {
      return add(items, selected);
    }
  };

  return [
    selected,
    {
      selectStatus,
      getIsSelected,
      toggle,
      toggleAll,
      select,
      unSelect,
      selectAll,
      unSelectAll,
    },
  ];
}

export default useSelections;
